package com.hmdp.utils;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.hmdp.dto.Result;
import com.hmdp.entity.Shop;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;

import static com.hmdp.utils.RedisConstants.CACHE_SHOP_KEY;

@Component
public class RedisCacheUtils {


    private final StringRedisTemplate stringRedisTemplate;

    public RedisCacheUtils(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 考虑缓存穿透的查询
     */

    /**
     * 泛型的使用：首先使用泛型，当然得声明泛型的名字(前面的<R>,尖括号里的就是声明，声明完就可以使用呢)，不然这个R就会被认为是一个类呢
     * 对于函数里要使用泛型，泛型声明在修饰符和返回值之间，给一个接口/类定义泛型，泛型声明在接口/类名后面，参看Function这种函数式接口的定义。
     * 除了泛型的声明，其他地方都是对声明的泛型的使用。
     */
    //泛型与Object
    //泛型的定义与使用
    public  <R, ID> R queryByPassThrough(String cacheKey, ID id, Class<R> ObjectClass, Function<ID,R> operation,
                                         TimeUnit timeUnit, Long time) {
        String info = stringRedisTemplate.opsForValue().get(cacheKey + id);
        if (!StringUtils.isEmpty(info)) {
            R r = JSONUtil.toBean(info, ObjectClass);
            return r;
        }

        if ("".equals(info)) {
            return null;
        }

        R result = operation.apply(id);

        if (result != null) {
            stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, JSONUtil.toJsonStr(result), time, timeUnit);
            return result;
        }
        stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, "", time ,timeUnit);
        return null;
    }




}
